package com.clei.utils.helper;

import com.clei.dto.PrintData;
import com.clei.enums.DatabaseServerEnum;
import com.clei.utils.CollectionUtil;
import com.clei.utils.MybatisUtil;
import com.clei.utils.PrintUtil;
import com.clei.utils.StringUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * table data printer
 * 表格数据对齐打印机
 *
 * @author Y
 * @date 2023-11-16
 **/
public class TableDataPrinter {

    /**
     * 英文字符宽度
     */
    private final static int EN_CHAR_WIDTH = StringUtil.width(' ');

    /**
     * 英文字符宽度一半
     */
    private final static int EN_CHAR_WIDTH_HALF = EN_CHAR_WIDTH / 2;

    /**
     * 包装字符
     */
    private final static char WRAPPER = '|';

    /**
     * null -> String
     */
    private final static String NULL_STR = "NULL";

    /**
     * dbHelper
     */
    private DBHelper dbHelper;

    /**
     * true使用tab false使用空格
     */
    private final boolean useTab;

    /**
     * 包裹value，不被空白符干扰
     */
    private final boolean wrapValue;

    public TableDataPrinter() {
        this(true, false);
    }

    public TableDataPrinter(DatabaseServerEnum dbServer) {
        this(dbServer, true, false);
    }

    public TableDataPrinter(DatabaseServerEnum dbServer, boolean useTab, boolean wrapValue) {
        this(useTab, wrapValue);
        this.dbHelper = dbServer.getDbHelper();
        MybatisUtil.setDb(dbServer);
    }

    public TableDataPrinter(boolean useTab, boolean wrapValue) {
        this.useTab = useTab;
        this.wrapValue = wrapValue;
    }

    /**
     * print
     *
     * @param querySql querySql
     */
    public void printDbData(String querySql) {
        List<Map<String, Object>> rowList = MybatisUtil.commonSingleOperation(mapper -> mapper.selectMap(querySql));
        printDbData(rowList);
    }

    /**
     * print
     *
     * @param columnList columnList
     * @param querySql   querySql
     */
    public void printDbData(List<String> columnList, String querySql) {
        List<Map<String, Object>> rowList = MybatisUtil.commonSingleOperation(mapper -> mapper.selectMap(querySql));
        printDbData(columnList, rowList);
    }

    /**
     * print
     *
     * @param stringRowList stringRowList
     */
    public void print(List<List<String>> stringRowList) {
        stringRowList = wrapIfNecessary(stringRowList, this.wrapValue);
        if (this.useTab) {
            printWithTab(stringRowList);
        } else {
            printWithSpace(stringRowList);
        }
    }

    /**
     * 打印
     *
     * @param titleList 标题
     * @param dataList  数据
     */
    public void print(List<String> titleList, List<? extends PrintData> dataList) {
        List<List<String>> printList = new ArrayList<>(dataList.size() + 1);
        printList.add(titleList);
        printList.addAll(dataList.stream().map(PrintData::toListString).collect(Collectors.toList()));
        print(printList);
    }

    /**
     * 打印
     *
     * @param rowList rowList
     */
    private void printDbData(List<Map<String, Object>> rowList) {
        if (CollectionUtil.isEmpty(rowList)) {
            return;
        }
        Map<String, Object> firstRow = rowList.get(0);
        // 默认顺序是keySet的迭代顺序
        List<String> columnList = new ArrayList<>(firstRow.keySet());
        printDbData(columnList, rowList);
    }

    /**
     * 打印
     *
     * @param columnList columnList
     * @param rowList    rowList
     */
    private void printDbData(List<String> columnList, List<Map<String, Object>> rowList) {
        if (CollectionUtil.isEmpty(columnList)) {
            return;
        }
        // 合并数据
        List<List<String>> stringRowList = Stream.concat(
                        Stream.of(columnList),
                        rowList.stream().map(row -> columnList.stream().map(c -> dbHelper.getString(row.get(c)))
                                .collect(Collectors.toList())))
                .collect(Collectors.toList());
        // 打印
        print(stringRowList);
    }

    /**
     * 打印
     *
     * @param stringRowList 数据
     */
    private static void printWithSpace(List<List<String>> stringRowList) {
        int columnCount = stringRowList.get(0).size();
        // 最大中英文字符数
        int[] maxEnCharArr = new int[columnCount];
        int[] maxCnCharArr = new int[columnCount];
        int[] maxLength = new int[columnCount];
        // 中英文字符数
        int[][][] charCountArr = new int[stringRowList.size()][columnCount][2];
        // 取得最适合的长度
        for (int o = 0; o < stringRowList.size(); o++) {
            List<String> row = stringRowList.get(o);
            for (int i = 0; i < columnCount; i++) {
                String str = row.get(i);
                if (null == str) {
                    str = NULL_STR;
                }
                int enCount = 0;
                int cnCount = 0;
                for (char c : str.toCharArray()) {
                    if (StringUtil.isEn(c)) {
                        enCount++;
                    } else {
                        cnCount++;
                    }
                }
                charCountArr[o][i][0] = enCount;
                charCountArr[o][i][1] = cnCount;
                maxEnCharArr[i] = Math.max(maxEnCharArr[i], enCount);
                maxCnCharArr[i] = Math.max(maxCnCharArr[i], cnCount);
                maxLength[i] = Math.max(maxLength[i], str.length());
            }
        }
        // 设置间隔空间
        for (int i = 0; i < columnCount; i++) {
            maxLength[i] += 4;
            maxEnCharArr[i] += 4;
        }
        // 中英文空格
        char enSpace = ' ';
        char cnSpace = '　';
        // 打印
        for (int o = 0; o < stringRowList.size(); o++) {
            List<String> row = stringRowList.get(o);
            for (int i = 0; i < columnCount; i++) {
                String value = row.get(i);
                if (null == value) {
                    value = NULL_STR;
                }
                if (0 == maxCnCharArr[i]) {
                    value = StringUtil.complete(value, enSpace, maxLength[i], false);
                } else {
                    int toCnLength = value.length() + maxCnCharArr[i] - charCountArr[o][i][1];
                    value = StringUtil.complete(value, cnSpace, toCnLength, false);
                    int toEnLength = value.length() + maxEnCharArr[i] - charCountArr[o][i][0];
                    value = StringUtil.complete(value, enSpace, toEnLength, false);
                }
                PrintUtil.print(value);
            }
            PrintUtil.println();
        }
    }

    /**
     * 打印
     *
     * @param stringRowList 数据
     */
    private static void printWithTab(List<List<String>> stringRowList) {
        int columnCount = stringRowList.get(0).size();
        // 长宽获取
        int[] maxLength = new int[columnCount];
        int[][] widthArr = new int[stringRowList.size()][columnCount];
        for (int o = 0; o < stringRowList.size(); o++) {
            List<String> row = stringRowList.get(o);
            for (int i = 0; i < columnCount; i++) {
                String str = row.get(i);
                if (null == str) {
                    str = NULL_STR;
                }
                int width = StringUtil.width(str);
                widthArr[o][i] = width;
                maxLength[i] = Math.max(maxLength[i], width);
            }
        }
        // 一个tab的最大长度，4代表4个空格(一个tab等于4个空格)，9代表当前Console Font下的size设置下单个英文空格的宽度
        int tabMaxLength = 4 * EN_CHAR_WIDTH;
        for (int i = 0; i < columnCount; i++) {
            int targetLength = maxLength[i] + tabMaxLength;
            int tabCount = targetLength / tabMaxLength;
            if (0 != targetLength % tabMaxLength) {
                tabCount++;
            }
            maxLength[i] = tabCount;
        }
        // 设置间隔空间
        char tabSpace = '\t';
        // 打印
        for (int o = 0; o < stringRowList.size(); o++) {
            List<String> row = stringRowList.get(o);
            for (int i = 0; i < columnCount; i++) {
                String value = row.get(i);
                if (null == value) {
                    value = NULL_STR;
                }
                // 多次试验证明 这个宽度的差距可以忽略
                // int curTabCount = widthArr[o][i] / tabMaxLength;
                int curTabCount = (widthArr[o][i] + EN_CHAR_WIDTH_HALF) / tabMaxLength;
                value = StringUtil.complete(value, tabSpace, value.length() + maxLength[i] - curTabCount, false);
                PrintUtil.print(value);
            }
            PrintUtil.println();
        }
    }

    /**
     * 包装
     *
     * @param stringRowList 数据
     * @param wrapValue     包裹value，不被空白符干扰
     * @return 数据
     */
    private static List<List<String>> wrapIfNecessary(List<List<String>> stringRowList, boolean wrapValue) {
        if (!wrapValue) {
            return stringRowList;
        }
        return stringRowList.stream()
                .map(l -> l.stream().map(s -> WRAPPER + s + WRAPPER).collect(Collectors.toList()))
                .collect(Collectors.toList());
    }
}
